//go:build !wasm

package knowninfo

import (
	"fmt"
	"go/parser"
	"go/token"
	"log/slog"
	"runtime"
	"slices"
	"strings"

	"github.com/puzpuzpuz/xsync/v4"
	"golang.org/x/sync/errgroup"

	"github.com/Zxilly/go-size-analyzer/internal/entity"
	"github.com/Zxilly/go-size-analyzer/internal/utils"
)

func (m *Dependencies) UpdateImportBy() {
	slog.Info("Analyzing package imports...")

	var (
		maxWorkers = runtime.NumCPU()
		eg         = errgroup.Group{}
	)
	eg.SetLimit(maxWorkers)

	importedBy := xsync.NewMap[string, utils.Set[string]]()

	_ = m.Trie.Walk(func(_ string, pkg *entity.Package) error {
		eg.Go(func() error {
			fset := token.NewFileSet()
			imports := utils.NewSet[string]()
			for _, f := range pkg.Files {
				if f.FilePath == "" || f.FilePath == "<autogenerated>" {
					// not a real file
					continue
				}

				if !strings.HasSuffix(f.FilePath, ".go") {
					// for .s no need to check
					continue
				}

				// check for import statements
				pf, err := parser.ParseFile(fset, f.FilePath, nil, parser.ImportsOnly)
				if err != nil {
					slog.Warn(fmt.Sprintf("failed to parse package %s file %s: %v", pkg.Name, f.FilePath, err))
					continue
				}
				for _, imp := range pf.Imports {
					pname := strings.Trim(imp.Path.Value, `"`)
					imports.Add(pname)
				}
			}

			for _, imp := range imports.ToSlice() {
				importedBy.Compute(imp, func(oldValue utils.Set[string], loaded bool) (newValue utils.Set[string], op xsync.ComputeOp) {
					if loaded {
						newValue = oldValue
					} else {
						newValue = utils.NewSet[string]()
					}
					newValue.Add(pkg.Name)
					op = xsync.UpdateOp
					return newValue, op
				})
			}
			return nil
		})

		return nil
	})

	_ = eg.Wait()

	_ = m.Trie.Walk(func(_ string, pkg *entity.Package) error {
		fs, ok := importedBy.Load(pkg.Name)
		if !ok {
			return nil
		}
		ps := fs.ToSlice()
		slices.Sort(ps)
		pkg.ImportedBy = ps

		return nil
	})

	slog.Info("Package imports analysis completed.")
}
